/*
	file:   tword.c
	author: Anders Ma
	email:  xuejiao.ma@gmail.com
	date:   2010_1118
*/

#include "tword.h"

#ifndef _WIN32
static struct termios termios_inventory;
#endif

word_t *g_word_head = NULL;
word_t *g_word_tail = NULL;
int g_word_counter = 0;

static int depth;
static NODE_TYPE curr_node;
static int debug_enable = 0;

static void clean();

void
DBG_PRINT(const char *msg, ...)
{
	va_list fmtargs;
	char buf[MAX_BUFLINE_SIZE];

	if (!debug_enable)
		return;
	va_start(fmtargs, msg);
	vsnprintf(buf, sizeof(buf)-1, msg, fmtargs);
	va_end(fmtargs);
	printf("%s",buf);
}

void* 
DMALLOC(int size)
{
	void *p = malloc(size);
	memset(p, 0, size);
#ifdef DEBUG_MEM
	DBG_PRINT("DMALLOC -> %x\n", p);
#endif
	return p;
}

void 
DFREE(void *ptr)
{
	free(ptr);
#ifdef DEBUG_MEM
	DBG_PRINT("DFREE <- %x\n", ptr);
#endif
}

void 
echo_off(void)
{
#ifndef _WIN32
	struct termios new_settings;
 	
	tcgetattr(0,&termios_inventory);
	new_settings = termios_inventory;
	new_settings.c_lflag &= ~(ECHO | ICANON);
	tcsetattr(0,TCSANOW,&new_settings);
	
	return;
#endif
}

void 
echo_on(void)
{
#ifndef _WIN32
	tcsetattr(0,TCSANOW,&termios_inventory);
	return;
#endif
}

void
sigint(int dummy)
{
	clean();
	echo_on();

	exit(1);
}

void
sigterm(int dummy)
{
	clean();
	echo_on();

	exit(1);
}

static void 
signal_init()
{
#ifndef _WIN32	
	struct sigaction sa;
	
	sa.sa_flags = 0;
	
	sigemptyset(&sa.sa_mask);
	sigaddset(&sa.sa_mask, SIGTERM);
	sigaddset(&sa.sa_mask, SIGINT);
	
	sa.sa_handler = sigterm;
	sigaction(SIGTERM, &sa, NULL);
	
	sa.sa_handler = sigint;
	sigaction(SIGINT, &sa, NULL);
#endif
}

static WORD_LANG
get_word_lang(const char* lang)
{
	if (!strcmp(lang, STR_LANG_EN))
		return LANG_EN;
	if (!strcmp(lang, STR_LANG_CN))
		return LANG_CN;
}

static WORD_TYPE
get_word_type(const char* type)
{
	if (!strcmp(type, STR_WORD_NOUN))
		return WORD_NOUN;
	if (!strcmp(type, STR_WORD_VERB))
		return WORD_VERB;
	if (!strcmp(type, STR_WORD_VI))
		return WORD_VI;
	if (!strcmp(type, STR_WORD_VT))
		return WORD_VT;
	if (!strcmp(type, STR_WORD_ADJ))
		return WORD_ADJ;
	if (!strcmp(type, STR_WORD_ADV))
		return WORD_ADV;

	return WORD_UNKNOWN;
}

char*
get_word_type_str(WORD_TYPE type)
{
	switch (type) {
	case WORD_NOUN:
		return STR_WORD_NOUN;
	case WORD_VERB:
		return STR_WORD_VERB;
	case WORD_VI:
		return STR_WORD_VI;
	case WORD_VT:
		return STR_WORD_VT;
	case WORD_ADJ:
		return STR_WORD_ADJ;
	case WORD_ADV:
		return STR_WORD_ADV;
	default:
		return NULL;
	}
}

static void XMLCALL
start (void *data, const char *el, const char **attr)
{

	int i;
	char *l = NULL; // lang
	char *m = NULL; // mean
	char *n = NULL; // name
	char *t = NULL; // type
	void *p = NULL;
	void *q = NULL;

	curr_node = UNKNOWN_NODE;

	for (i = 0; i < depth; i++)
		DBG_PRINT ("  ");

	DBG_PRINT("%s", el);
	if (!strcmp(el, TAG_WORD)) {
		curr_node = WORD_NODE;
		for (i = 0; attr[i]; i += 2) {
			DBG_PRINT(" %s='%s'", attr[i], attr[i + 1]);
			if (!strcmp(attr[i], ATTR_NAME)) {
				n = (char*)attr[i+1];
				continue;
			} else if (!strcmp(attr[i], ATTR_LANG)) {
				l = (char*)attr[i+1];
				continue;
			}
		}
		p = DMALLOC(sizeof(word_t));
		if (!p) {
			fprintf(stderr, "Couldn't allocate memory for word\n");
			return;
		}
		if (WORD_HEAD == NULL) {
			WORD_HEAD = (word_t*)p;
		}
		q = (void*)WORD_TAIL;
		WORD_TAIL = (word_t*)p;
#ifdef _WIN32
		WORD_TAIL->name = strndup(n, strlen(n));
#else
		WORD_TAIL->name = strdup(n);
#endif
		WORD_TAIL->lang = get_word_lang(l);
		if (q != NULL) {
			((word_t*)q)->next = WORD_TAIL; 
		}
		INC_WORD_cTR();
	}  else if (!strcmp(el, TAG_SLOT)) {
		curr_node = SLOT_NODE;
		for (i = 0; attr[i]; i += 2) {
			DBG_PRINT(" %s='%s'", attr[i], attr[i + 1]);
			if (!strcmp(attr[i], ATTR_TYPE)) {
				t = (char*)attr[i+1];
				continue;
			} else if (!strcmp(attr[i], ATTR_LANG)) {
				l = (char*)attr[i+1];
				continue;
			} else if (!strcmp(attr[i], ATTR_MEAN)) {
				m = (char*)attr[i+1];
				continue;
			}
		}
		p = DMALLOC(sizeof(slot_t));
		if (!p) {
			fprintf(stderr, "Couldn't allocate memory for slot\n");
			return;
		}
		q = (void*)SLOT;
		SLOT = (slot_t*)p;
		SLOT->type = get_word_type(t);
		SLOT->lang = get_word_lang(l);
#ifdef _WIN32
		SLOT->mean = strndup(m, strlen(m));
#else
		SLOT->mean = strdup(m);
#endif
		SLOT->next = (slot_t*)q;
	} else if (!strcmp(el, TAG_EXAMPLE)) {
		curr_node = EXAMPLE_NODE;
	}

	depth++;

}

static void XMLCALL
end (void *data, const char *el)
{
	depth--;
}

static void XMLCALL
data(void *data, const char *cont, int len)
{
	int i = 0;

#if 1
	DBG_PRINT("  ");
	while(i < len) {
		DBG_PRINT("%c", *(cont + i));
		i++;
	}
#endif
	if (curr_node == EXAMPLE_NODE) {
		if (!EXAMPLE)
			EXAMPLE = strndup(cont, len);
	}
}

static void
clean()
{
	word_t *w, *wn;
	slot_t *s, *sn;

	int k, l;

	DBG_PRINT("->TWORD\n-------\n");
	for (k = 1, w = WORD_HEAD; w != NULL;) {
		DBG_PRINT("    ->[word] %s\n", w->name);
		wn = w->next;
		for (l = 1, s = w->slot_list; s != NULL;) {
			DBG_PRINT("      ->[slot] %s\n"
					"               %s\n"
					"               eg: %s\n",
					get_word_type_str(s->type), 
					s->mean, s->example);
			sn = s->next;
			if (s->mean)
				DFREE(s->mean);
			if (s->example)
				DFREE(s->example);
			DFREE(s);
			s = sn;
			l++;
		}
		if (w->name)
			DFREE(w->name);				
		DFREE(w);
		w = wn;
		k++;
	}
	fprintf(stderr, "See you next time!\n");
}

static void
enable_debug()
{
	debug_enable = 1;
}

static void
version()
{
	fprintf(stderr, "\nVersion: %d.%d(%s)\n", 
		VERSION_MAJOR, VERSION_MINOR, VERSION_DATE);
	fprintf(stderr, " Author: Anders Ma(xuejiao.ma@gmail.com)\n\n");

}

static void
usage()
{
	fprintf(stderr, "tword [option]\n");
	fprintf(stderr, "\t -d      enable debug\n");
	fprintf(stderr, "\t -f      word file\n");
	fprintf(stderr, "\t -h      this usage info\n");
	fprintf(stderr, "\t -r [n]  random test mode\n");
	fprintf(stderr, "\t -v      show version info\n");

}

int
main (int argc, char *argv[])
{
	extern char *optarg;
	extern int optind;
	extern int optopt;
	extern int opterr;
	extern int optreset;

	FILE *fp;
	XML_Parser p;
	char buf[MAX_BUFLINE_SIZE];
	char file[MAX_FILE_NAME_SIZE] = DEF_WORD_FILE_NAME;
	char *str;

	char *optstr="df:hr::v";
	int opt = 0;
	int rtmode = 0, rtnumber = RANDOM_TEST_BATCH_OUTPUT_NO;

#ifdef _WIN32
	for (opt = 1; opt < argc; opt++) {
		if (argv[opt][0] == '-') {
			switch (argv[opt][1]) {
			case 'd':
				enable_debug();
				break;
			case 'f':
				strncpy(file, argv[opt + 1], MAX_FILE_NAME_SIZE);
				break;
			case 'r':
				rtmode = 1;
				if (argv[opt][2]) {
					rtnumber = atoi(&argv[opt][2]);
				}
				break;
			case 'v':
				version();
				return 0;
			case 'h':
			default:
				usage();
				return 0;
			}
		}
	}
#else
	opt = getopt( argc, argv, optstr);
	while( opt != -1 ) {
		switch( opt ) {
		case 'd':
			enable_debug();
			break;
		case 'f':
			strncpy(file, optarg, MAX_FILE_NAME_SIZE);
			break;
		case 'r':
			rtmode = 1;
			if (optarg)
				rtnumber = atoi(optarg);
			break;
		case 'v':
			version();
			return 0;
		case 'h':
			usage();
			return 0;
		default:
			usage();
		        return 0;
		}
		opt = getopt(argc, argv, optstr);
	}
#endif
	signal_init();

	fp = fopen(file, "r");
	if (!fp) {
		fprintf(stderr, "Couldn't open file %s\n", file);
		exit(-1);		
	}
	p = XML_ParserCreate(NULL);
	if (!p) {
		fprintf(stderr, "Couldn't allocate memory for XML parser\n");
		fclose(fp);
		exit (-1);
	}
	XML_SetElementHandler (p, start, end);
	XML_SetCharacterDataHandler(p, data);
	
	for (;;) {
		int done;
		int len;

		str = fgets(buf, MAX_BUFLINE_SIZE, fp);
		len = strlen(buf);
		done = feof(fp);

		if (XML_Parse (p, buf, len, done) == XML_STATUS_ERROR) {
			fprintf(stderr, "Parse error at line %" 
					XML_FMT_INT_MOD "u:\n%s\n",
              				XML_GetCurrentLineNumber(p),
              				XML_ErrorString(XML_GetErrorCode(p)));
			fclose(fp);
			clean();
			exit (-1);
		}

		if (done)
			break;
	}
	XML_ParserFree (p);
	echo_off();
	if (rtmode) {
		do_random_test(LANG_EN, LANG_CN, rtnumber);
	}
	fclose(fp);
	clean();
	echo_on();

	return 0;
}
